package zmcc.bsp.fabric.network;

import zmcc.bsp.fabric.common.constants.SymbolConstants;
import zmcc.bsp.fabric.exception.NetworkBuilderException;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Configuration
@ConfigurationProperties(prefix = "spring.fabric")
@PropertySource(value = "classpath:generator.properties")
public class NetworkGenerator {

    String domain;

    String networkName;

    String orgNames;

    String chaincodes;

    String ordererOrg;

    List<String> channelPeers ;

    String channels;

    String networkFileRootPath;

    String certFileRootPath;

    String peerUrl;

    String orderUrl;

    Map<String, String> orderers;

    Map<String, Map<String,String>> peers;


    public void generateConnectionYmlFile() throws NetworkBuilderException {

        if (chaincodes == null || chaincodes.trim().length() == 0) {
            throw new NetworkBuilderException("The network peers is not specified.");
        }
        if (networkFileRootPath == null || networkFileRootPath.trim().length() == 0) {
            throw new NetworkBuilderException("The network file root path is not specified.");
        }
        if (certFileRootPath == null || certFileRootPath.trim().length() == 0) {
            throw new NetworkBuilderException("The cert file root path is not specified.");
        }
        if (orgNames == null || orgNames.trim().length() == 0) {
            throw new NetworkBuilderException("The network orgName is not specified.");
        }
        String[] clients = chaincodes.split(SymbolConstants.COMMA_SYMBOL);
        File root = new File("").getAbsoluteFile();
        for (String orgName : orgNames.split(SymbolConstants.COMMA_SYMBOL)) {
            for (int i = 0; i < clients.length; i++) {
                //待添加mysql查询
                NetworkBuilder builder = buildInit();

                buildOrderers(builder);

                buildOrg(builder, orgName);
                JsonObject json;
                try {
                    json = builder.root(new File(root, certFileRootPath)).build();
                } catch (NetworkBuilderException networkException) {
                    networkException.printStackTrace();
                    continue;
                }
                Gson gson = new Gson();
                String value = gson.toJson(json);

                Yaml yaml = new Yaml();
                Object map = yaml.load(value);
                String yvalue = yaml.dump(map);
                try {
                    File file = new File(root, networkFileRootPath + clients[i] + "-" + orgName + ".yml");
                    if (!file.exists()) {
                        Files.write(file.toPath(), yvalue.getBytes(), StandardOpenOption.CREATE_NEW,
                                StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
                    } else {
                        Files.write(file.toPath(), yvalue.getBytes(), StandardOpenOption.TRUNCATE_EXISTING,
                                StandardOpenOption.WRITE);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public InputStream getResourceFromJar(String fileName) throws IOException {
        InputStream is = this.getClass().getResourceAsStream("/" + fileName);
        return is;
    }


    private NetworkBuilder buildInit() throws NetworkBuilderException {

        if (channels == null || channels.trim().length() == 0) {
            throw new NetworkBuilderException("The network channel is not specified.");
        }
        return new NetworkBuilder(domain).name(networkName)
                .channels(channels.split(SymbolConstants.COMMA_SYMBOL))
                ;
    }

    private NetworkBuilder buildOrg(NetworkBuilder builder, String orgName) throws NetworkBuilderException {
        if (peerUrl == null || peerUrl.trim().length() == 0) {
            throw new NetworkBuilderException("The network peer url is not specified.");
        }
        if (peers == null || peers.size() == 0) {
            throw new NetworkBuilderException("The network peers is not specified.");
        }
        String[] orgPeerArray = peers.keySet().toArray(new String[0]);
        for (String org : orgPeerArray) {
            if (org.equals(orgName)){
                channelPeers = new ArrayList<>();
                Map<String, String> peerArray = peers.get(org);
                for (String peer:peerArray.keySet()){
                    channelPeers.add(peer);
                    builder.port(orgName, peer,Integer.parseInt(peerArray.get(peer)) );
                }
                builder.peers(channelPeers.toArray(new String[0]));
            }
        }
        return builder.peerOrgs(orgName)
                .clientOrg(orgName)
                .url(orgName, "*", peerUrl);
    }

    private NetworkBuilder buildOrderers(NetworkBuilder builder) throws NetworkBuilderException {
        if (orderers == null || orderers.size() == 0) {
            throw new NetworkBuilderException("The network orderers is not specified.");
        }
        if (ordererOrg == null || ordererOrg.trim().length() == 0) {
            throw new NetworkBuilderException("The network orderer name is not specified.");
        }
        if (orderUrl == null || orderUrl.trim().length() == 0) {
            throw new NetworkBuilderException("The network orderer url is not specified.");
        }
        String[] peers = orderers.keySet().toArray(new String[0]);
        builder.orderers(peers);
        for (String peer : peers) {
            builder.port(ordererOrg, peer, Integer.parseInt(orderers.get(peer)));
        }
        return builder.ordererOrg(ordererOrg).url(ordererOrg, "*", orderUrl);
    }

    public String getChaincodes() {
        return chaincodes;
    }

    public void setChaincodes(String chaincodes) {
        this.chaincodes = chaincodes;
    }

    public String getDomain() {
        return domain;
    }

    public void setDomain(String domain) {
        this.domain = domain;
    }

    public String getOrgNames() {
        return orgNames;
    }

    public void setOrgNames(String orgNames) {
        this.orgNames = orgNames;
    }

    public String getNetworkName() {
        return networkName;
    }

    public void setNetworkName(String networkName) {
        this.networkName = networkName;
    }

    public String getOrdererOrg() {
        return ordererOrg;
    }

    public void setOrdererOrg(String ordererOrg) {
        this.ordererOrg = ordererOrg;
    }

    public List<String> getChannelPeers() {
        return channelPeers;
    }

    public void setChannelPeers(List<String> channelPeers) {
        this.channelPeers = channelPeers;
    }

    public String getChannels() {
        return channels;
    }

    public void setChannels(String channels) {
        this.channels = channels;
    }

    public String getNetworkFileRootPath() {
        return networkFileRootPath;
    }

    public void setNetworkFileRootPath(String networkFileRootPath) {
        this.networkFileRootPath = networkFileRootPath;
    }

    public String getCertFileRootPath() {
        return certFileRootPath;
    }

    public void setCertFileRootPath(String certFileRootPath) {
        this.certFileRootPath = certFileRootPath;
    }

    public String getPeerUrl() {
        return peerUrl;
    }

    public void setPeerUrl(String peerUrl) {
        this.peerUrl = peerUrl;
    }

    public String getOrderUrl() {
        return orderUrl;
    }

    public void setOrderUrl(String orderUrl) {
        this.orderUrl = orderUrl;
    }

    public Map<String, String> getOrderers() {
        return orderers;
    }

    public void setOrderers(Map<String, String> orderers) {
        this.orderers = orderers;
    }

    public Map<String, Map<String, String>> getPeers() {
        return peers;
    }

    public void setPeers(Map<String, Map<String, String>> peers) {
        this.peers = peers;
    }
}
